/***************************************************************************
 * Copyright 2012-2013 TXT e-solutions SpA
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * This work was performed within the IoT_at_Work Project
 * and partially funded by the European Commission's
 * 7th Framework Programme under the research area ICT-2009.1.3
 * Internet of Things and enterprise environments.
 *
 * Authors:
 *      Salvatore Piccione (TXT e-solutions SpA)
 *
 * Contributors:
 *        Domenico Rotondi (TXT e-solutions SpA)
 **************************************************************************/
package it.txt.ens.authorisationService.util;

import it.txt.access.capability.commons.schema.validation.CapabilitySchemaValidationHandlerException;
import it.txt.access.capability.commons.signer.X509DocumentSigner;
import it.txt.access.capability.commons.signer.model.X509CertificateKeyValues;
import it.txt.access.capability.commons.signer.model.X509CertificateSubjectInfo;
import it.txt.access.capability.commons.utils.XMLPrinter;
import it.txt.access.capability.factory.CapabilitySchemaFactory;
import it.txt.access.capability.factory.CapabilitySchemaFactoryException;
import it.txt.access.capability.pdp.client.PDPClient;
import it.txt.access.capability.schema.AccessRightType;
import it.txt.access.capability.schema.AccessRightsCapabilityType;
import it.txt.access.capability.verifier.CapabilityVerifier;
import it.txt.access.capability.verifier.CapabilityVerifierException;
import it.txt.access.capability.verifier.VerifiedCapability;
import it.txt.ens.authorisationService.amqp.AMQPConnectionHelper;
import it.txt.ens.authorisationService.amqp.AMQPExchangeHelper;
import it.txt.ens.authorisationService.amqp.AMQPQueueHelper;
import it.txt.ens.authorisationService.amqp.ManagedBroker;
import it.txt.ens.authorisationService.amqp.ManagedNamespace;
import it.txt.ens.authorisationService.amqp.ManagedVirtualHost;
import it.txt.ens.authorisationService.gui.standalone.util.SimplePasswordGenerator;
import it.txt.ens.core.ENSResource;
import it.txt.ens.core.KeystoreParameters;
import it.txt.ens.core.X509CertificateRetrievalParameters;
import it.txt.ens.core.factory.ENSResourceFactory;
import it.txt.ens.core.factory.URIParseException;
import it.txt.ens.namespace.NamespaceInquirer;
import it.txt.ens.namespace.exception.NamespaceInquirerException;
import it.txt.ens.namespace.exception.SyntaxPatternException;
import it.txt.ens.schema.RequestType;
import it.txt.ens.schema.ResponseType;
import it.txt.ens.schema.factory.ENSRequestFactoryException;
import it.txt.ens.schema.factory.ENSResponseFactoryException;
import it.txt.ens.schema.factory.model.FailureResponseDetails;
import it.txt.ens.schema.factory.model.SuccessResponseDetails;
import it.txt.ens.schema.request.factory.ENSAuthorisationRequestFactory;
import it.txt.ens.schema.response.factory.ENSAuthorisationResponseFactory;
import it.txt.rabbitmq.management.rest.factory.PermissionServiceFactory;
import it.txt.rabbitmq.management.rest.factory.UserServiceFactory;
import it.txt.rabbitmq.management.rest.resources.Permission;
import it.txt.rabbitmq.management.rest.resources.User;
import it.txt.rabbitmq.management.rest.services.PermissionService;
import it.txt.rabbitmq.management.rest.services.UserService;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.crypto.MarshalException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;

import oasis.names.tc.saml._2_0.assertion.ActionType;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;
//import it.txt.ens.authorisationService.gui.standalone.util.ApplicationPropertiesRepository;

/**
 * This class evaluates an access request and send back to the service requester
 * a message with the processing outcome.
 * 
 * @author Carbone Matteo
 * @author Salvatore Piccione (TXT e-solutions SpA - salvatore.piccione AT network.txtgroup.com)
 */
class AccessRequestEvaluator implements Runnable,ShutdownListener {

//    private static final String PERMISSION_SEPARATOR = "|";
    private static final String CHARSET = "UTF-8";
    private static final long SESSION_DURATION = 1000 * 60 * 60 * 24 * 30;
    
    public static final String OPERATIVE_QUEUE_NAME_FORMAT = "ENS-queue-%1$s-%2$tQ";
    public static final String RABBITMQ_USERNAME_FORMAT = "ENS-%1$s-%2$tQ";
    public static ConcurrentMap<String,ManagedNamespace> namespaces;
    public static ConcurrentMap<String,ManagedVirtualHost> virtualHosts;
    public static ConcurrentMap<String,ManagedBroker> brokers;
    public static final String TRANSFORMER_FACTORY_CLASS_NAME = 
            "javax.xml.transform.TransformerFactory";
    public static final String DEFAULT_FACTORY_CLASS_NAME = 
            "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";
            
    
	private ENSAuthorisationResponseFactory ensResponseFactory;
	private ENSAuthorisationRequestFactory ensRequestFactory;
	private PDPClient pdpClient;
	private NamespaceInquirer namespaceInquirer;
	private UserServiceFactory rabbitMQUserSF;
	private PermissionServiceFactory rabbitMQPermissionSF;
	private ENSResourceFactory ensResourceFactory;
	
	private SimplePasswordGenerator pwdGen;
	//private FailureResponseDetails failureDetails;
	//private SuccessResponseDetails successDetails;
	private ResponseType responseType = null;
	private RequestType requestType = null;
	private String namespace;
	private String routingKey;
	private String operation;
	//private X509CertificateSubjectInfo x509CertificateSubjectInfo;
//	private String requestMsg;
//	private String keystorePwd;
//	private String keystorePath;
//	private String privateKeyPwd;
//	private String keystoreAlias;
    private KeystoreParameters keystoreParams;
    private X509CertificateRetrievalParameters certParams;
    private final byte[] accessRequest;
    private Connection connection;
    private BasicProperties requestProperties;
    private AccessRightsCapabilityType capability;
    private String operativeHost;
    private int operativePort;
    private volatile boolean isClosed;
    private String subjectID;

	private static final String INTERNAL_ERROR = "INTERNAL_ERROR";
	//private static final String EXCHANGE_SUFFIX = "_exchange";
	
	private final static Logger LOGGER = Logger.getLogger(AccessRequestEvaluator.class.getName()); 
	private static final ResourceBundle MESSAGES = ResourceBundle.getBundle(
	        "message-bundles/" + AccessRequestEvaluator.class.getSimpleName(), Locale.ROOT);
	
	public static final String OPERATION_SUBSCRIBE = "subscribe";
	public static final String OPERATION_PUBLISH = "publish";

    //thread introduced to not use the one provided by RabbitMQ
//    private static final ExecutorService THREAD_POOL = Executors.newSingleThreadExecutor();
   
	
	public AccessRequestEvaluator(ENSAuthorisationResponseFactory ensResponseFactory, 
            ENSAuthorisationRequestFactory ensRequestFactory,
            KeystoreParameters keystoreParams,
            X509CertificateRetrievalParameters certParams,
            ENSResourceFactory ensResourceFactory,
            PDPClient pdpClient,
            NamespaceInquirer namespaceInquirer,
            UserServiceFactory rabbitMQUserService,
            PermissionServiceFactory rabbitMQPermissionService,
//            ConcurrentMap<String,ManagedNamespace> namespaceMap,
//            ConcurrentMap<String,ManagedVirtualHost> virtualHostMap,
//            ConcurrentMap<String,ManagedBroker> brokerMap,
            byte[] accessRequest, Connection connection, BasicProperties properties) {
        this.ensRequestFactory = ensRequestFactory;
        this.ensResponseFactory = ensResponseFactory;
        this.keystoreParams = keystoreParams;
        this.certParams = certParams;
        this.pdpClient = pdpClient;
        this.namespaceInquirer = namespaceInquirer;
        this.rabbitMQUserSF = rabbitMQUserService;
        this.rabbitMQPermissionSF = rabbitMQPermissionService;
        this.pwdGen = new SimplePasswordGenerator(10);
        this.certParams = certParams;
//        this.namespaces = namespaceMap;
//        this.virtualHosts = virtualHostMap;
//        this.brokers = brokerMap;
        this.accessRequest = accessRequest;
        this.connection = connection;
        this.requestProperties = properties;
        this.ensResourceFactory = ensResourceFactory;
        this.isClosed = true;
    }

	/* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        Channel channel = null;
        String correlationId = requestProperties.getCorrelationId();
        String replyTo = requestProperties.getReplyTo();
        try {
            byte[] response = evaluate();
            channel = connection.createChannel();
            isClosed = false;
            channel.addShutdownListener(this);
            if (correlationId != null && replyTo != null) {
                AMQP.BasicProperties replyProperties
                    = new AMQP.BasicProperties.Builder().correlationId(correlationId).build();
                channel.basicPublish("", replyTo, replyProperties, response);
                if (LOGGER.isLoggable(Level.FINER))
                    LOGGER.log(Level.FINER, MESSAGES.getString("responsePublished"));
            } else {
                LOGGER.severe(MESSAGES.getString("missingCorrelationIDAndReplyTo"));
            }
//            channel.basicAck(envelope.getDeliveryTag(), false);
        } catch (IOException e) {
            LOGGER.log(Level.SEVERE, MessageFormat.format(MESSAGES.getString("responsePublishFailure"), 
                    correlationId, replyTo), e);
        } finally {
            if (channel != null)
                try {
                    isClosed = true;
                    channel.close();
                } catch (IOException e) {}
                catch (ShutdownSignalException e) {}
        }
    }

    public String evaluateToString () throws IOException {
        try {
            return new String(evaluate(), CHARSET);
        } catch (UnsupportedEncodingException e) {
            LOGGER.log(Level.SEVERE, MessageFormat.format(MESSAGES.getString("byteEncodingFailure"), CHARSET),e);
            return INTERNAL_ERROR;
        }
    }
    
    public byte[] evaluate()	throws IOException {
		if (LOGGER.isLoggable(Level.FINEST))
		    LOGGER.log(Level.FINEST, MESSAGES.getString("startingAccessRequestEvaluation"));
		
		Document responseDoc = null;
		
		try {
			requestType = ensRequestFactory.parseInputStream(new ByteArrayInputStream(accessRequest));
            //pretty print of the request
            if (LOGGER.isLoggable(Level.FINE))
                LOGGER.log(Level.FINE, MessageFormat.format(
                        MESSAGES.getString("receivedAccessRequestDetail"),
                        XMLPrinter.printDocumentNode(ensRequestFactory.marshal(requestType), true)));
            operation = requestType.getOperation();
            String resourceID = requestType.getResourceID();
            subjectID = requestType.getSubject().getSubjectID();
            ENSResource ensResource;
            try {
                ensResource = ensResourceFactory.create(new URI(resourceID));
                namespace = ensResource.getNamespace();
                routingKey = ensResource.getPattern();
                if (LOGGER.isLoggable(Level.INFO))
                    LOGGER.info(MessageFormat.format(MESSAGES.getString("receivedAccessRequest"),
                            subjectID, operation, namespace,routingKey));
                
                capability = requestType.getAccessRightsCapability().getCapability();
                if (LOGGER.isLoggable(Level.FINE))
                    LOGGER.fine(MESSAGES.getString("evaluationStep1"));
                
                if (subjectID.equalsIgnoreCase(capability.getSubject().getSubjectID().getValue())) {
                    if (LOGGER.isLoggable(Level.FINE))
                        LOGGER.fine(MESSAGES.getString("evaluationStep2"));
                    if (operation.equalsIgnoreCase(OPERATION_PUBLISH) || 
                            operation.equalsIgnoreCase(OPERATION_SUBSCRIBE)) {
                        if (LOGGER.isLoggable(Level.FINE))
                            LOGGER.fine(MESSAGES.getString("evaluationStep3"));
                        List<AccessRightType> accessRights = capability.getAccessRights().getAccessRight();
                        if (isOperationAmongGrantedRights(accessRights, operation)) {
                            // Verify the capability through the CapabilityVerifier
                            if(executeCapabilityVerifierCheck()){
                                if(executePDPCapabilityCheck()){
                                    if(executeNamespaceCapabilityCheck()){
                                        executeRabbitMQOperations();
                                    }
                                }
                            }
                        } else {
                            FailureResponseDetails failureDetails = new FailureResponseDetails();
                            String reason = MESSAGES.getString(
                                    ErrorCodes.REQUESTED_OPERATION_IS_NOT_GRANTED_RIGHT);
                            failureDetails.setErrorReason(reason);
                            failureDetails.setErrorCode(ErrorCodes.REQUESTED_OPERATION_IS_NOT_GRANTED_RIGHT);
                            responseType = ensResponseFactory.createFailureResponse(
                                    requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
                            if (LOGGER.isLoggable(Level.INFO))
                                LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep3Failure"), 
                                        ErrorCodes.REQUESTED_OPERATION_IS_NOT_GRANTED_RIGHT, reason,
                                        operation, printAccessRightsList(accessRights)));
                        }
                    }
                    else {
                        FailureResponseDetails failureDetails = new FailureResponseDetails();
                        String reason = MESSAGES.getString(
                                ErrorCodes.ENS_OPERATION_NOT_MANAGED);
                        failureDetails.setErrorReason(reason);
                        failureDetails.setErrorCode(ErrorCodes.ENS_OPERATION_NOT_MANAGED);
                        responseType = ensResponseFactory.createFailureResponse(
                                requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
                        if (LOGGER.isLoggable(Level.INFO))
                            LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep2Failure"), 
                                    ErrorCodes.ENS_OPERATION_NOT_MANAGED, reason,
                                    operation));
                    }
                } else {
                    FailureResponseDetails failureDetails = new FailureResponseDetails();
                    String reason = MESSAGES.getString(ErrorCodes.REQUESTOR_NOT_EQUAL_TO_CAPABILITY_SUBJECT);
                    failureDetails.setErrorReason(reason);
                    failureDetails.setErrorCode(ErrorCodes.REQUESTOR_NOT_EQUAL_TO_CAPABILITY_SUBJECT);
                    responseType = ensResponseFactory.createFailureResponse(
                            requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
                    if (LOGGER.isLoggable(Level.INFO))
                        LOGGER.info(MessageFormat.format("evaluationStep1Failure", 
                            ErrorCodes.REQUESTOR_NOT_EQUAL_TO_CAPABILITY_SUBJECT, reason));
                }
            } catch (URIParseException e) {
                FailureResponseDetails failureDetails = new FailureResponseDetails();
                String reason = MESSAGES.getString(ErrorCodes.CAPABILITY_RESOURCE_ID_PARSING_FAILURE);
                failureDetails.setErrorReason(reason);
                failureDetails.setErrorCode(ErrorCodes.CAPABILITY_RESOURCE_ID_PARSING_FAILURE);
                responseType = ensResponseFactory.createFailureResponse(
                    requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
                LOGGER.log(Level.SEVERE, MessageFormat.format(MESSAGES.getString("genericError"),
                        ErrorCodes.CAPABILITY_RESOURCE_ID_PARSING_FAILURE,reason),e);
            } catch (URISyntaxException e) {
                FailureResponseDetails failureDetails = new FailureResponseDetails();
                String reason = MESSAGES.getString(ErrorCodes.CAPABILITY_RESOURCE_ID_PARSING_FAILURE);
                failureDetails.setErrorReason(reason);
                failureDetails.setErrorCode(ErrorCodes.CAPABILITY_RESOURCE_ID_PARSING_FAILURE);
                responseType = ensResponseFactory.createFailureResponse(
                        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
                LOGGER.log(Level.SEVERE, MessageFormat.format(MESSAGES.getString("genericError"),
                        ErrorCodes.CAPABILITY_RESOURCE_ID_PARSING_FAILURE,reason),e);
            }

			if (responseType != null) {
    			if (responseType.isResult()) {
    			    LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationOK"), 
    			            subjectID, operation, namespace, routingKey));
    			}
			}
			
			X509CertificateKeyValues keyValues = X509DocumentSigner.getCertificateKeyValues(
			        keystoreParams.getKeystorePath().getAbsolutePath(),
			        keystoreParams.getKeystorePassword(),
			        //indeed this is the alias not the certificate subject
			        certParams.getX509CertificateSubject(),
			        certParams.getPrivateKeyPassword());
			responseDoc = ensResponseFactory.marshal(responseType);
			X509DocumentSigner.signXMLElement(responseDoc.getDocumentElement(),
			        keyValues,ENSAuthorisationResponseFactory.SIGN_DOCUMENT_AFTER_NODE);

			//pretty print of the response
			if (LOGGER.isLoggable(Level.FINE))
			    LOGGER.log(Level.FINE, MessageFormat.format(
			            MESSAGES.getString("accessResponse"),
			            XMLPrinter.printDocumentNode(responseDoc, true)));
			
			ByteArrayOutputStream xmlos = (ByteArrayOutputStream) XMLPrinter.convertDOMIntoByteStream(
			        responseDoc, false, null, new ByteArrayOutputStream()).getOutputStream(); 
			        new ByteArrayOutputStream();
			return xmlos.toByteArray();
		}  catch (GeneralSecurityException e) {
		    LOGGER.log(Level.SEVERE, "Internal error:", e);
		} catch (TransformerException e) {
		    LOGGER.log(Level.SEVERE, "Internal error:", e);
		} catch (TransformerFactoryConfigurationError e) {
			LOGGER.log(Level.SEVERE, "Internal error:", e);
        } catch (ENSResponseFactoryException e) {
            LOGGER.log(Level.SEVERE, "Internal error:", e);
		} catch (ENSRequestFactoryException e) {
		    LOGGER.log(Level.SEVERE, "Internal error:", e);
		} 
		if (LOGGER.isLoggable(Level.FINE))
		    LOGGER.log(Level.FINE, MessageFormat.format(
		            MESSAGES.getString("accessResponse"),
		            INTERNAL_ERROR));
        return INTERNAL_ERROR.getBytes(CHARSET);
	}
    
    /**
     * @param accessRights
     * @return
     */
    private String printAccessRightsList(List<AccessRightType> accessRights) {
        StringBuilder builder = new StringBuilder();
        ActionType operation;
        String namespace;
        for (AccessRightType accessRight : accessRights) {
            operation = accessRight.getPermittedAction();
            if (builder.length() > 0)
                builder.append(", ");
            namespace = operation.getNamespace();
            if (namespace != null) {
                builder.append(namespace);
                builder.append(":");
            }
            builder.append(operation.getValue());
        }
        return builder.toString();
    }

    private boolean isOperationAmongGrantedRights (List<AccessRightType> rights, String operation) {
        //At the moment we don't care about conditions
        boolean found = false;
        int i = 0;
        while (!found && i < rights.size()) {
            if (operation.equalsIgnoreCase(rights.get(i).getPermittedAction().getValue()))
                found = true;
            else
                i++;
        }
        return found;
    }
	
	/**
	 * @throws ENSResponseFactoryException 
	 * @throws ENSServiceFactoryException 
	 * 
	 */
	private void executeRabbitMQOperations() throws ENSResponseFactoryException {
	    if (LOGGER.isLoggable(Level.FINE))
	        LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep8"), namespace));
	    
		ManagedNamespace mNamespace = namespaces.get(namespace);
		if (mNamespace == null) {
		    FailureResponseDetails failureDetails = new FailureResponseDetails();
            String reason = MESSAGES.getString(ErrorCodes.NAMESPACE_NOT_MANAGED);
            failureDetails.setErrorReason(reason);
            failureDetails.setErrorCode(ErrorCodes.NAMESPACE_NOT_MANAGED);
            responseType = ensResponseFactory.createFailureResponse(
                    requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
            if (LOGGER.isLoggable(Level.INFO))
                LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep8Failure"),
                        ErrorCodes.NAMESPACE_NOT_MANAGED, reason, namespace));
            return;
		}
		if (LOGGER.isLoggable(Level.FINE))
		    LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep8OK1"), 
		            mNamespace.getNamespaceAMQPExchangeName()));
	    
		ManagedVirtualHost virtualHost = virtualHosts.get(mNamespace.getVirtualHostName());
	    if (virtualHost == null) {
	        FailureResponseDetails failureDetails = new FailureResponseDetails();
            String reason = MESSAGES.getString(ErrorCodes.VIRTUAL_HOST_NOT_MANAGED);
            failureDetails.setErrorReason(reason);
            failureDetails.setErrorCode(ErrorCodes.VIRTUAL_HOST_NOT_MANAGED);
            responseType = ensResponseFactory.createFailureResponse(
                    requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
            if (LOGGER.isLoggable(Level.INFO))
                LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep8Failure"),
                        ErrorCodes.VIRTUAL_HOST_NOT_MANAGED, reason, mNamespace.getVirtualHostName()));
            return;
	    }
	    if (LOGGER.isLoggable(Level.FINE))
            LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep8OK2"), 
                    virtualHost));
	    
	    ManagedBroker broker = brokers.get(virtualHost.getHostingBrokerName());
	    if (broker ==  null) {
	        FailureResponseDetails failureDetails = new FailureResponseDetails();
            String reason = MESSAGES.getString(ErrorCodes.BROKER_NOT_MANAGED);
            failureDetails.setErrorReason(reason);
            failureDetails.setErrorCode(ErrorCodes.BROKER_NOT_MANAGED);
            responseType = ensResponseFactory.createFailureResponse(
                    requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
            if (LOGGER.isLoggable(Level.INFO))
                LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep8Failure"),
                        ErrorCodes.BROKER_NOT_MANAGED, reason, virtualHost.getHostingBrokerName()));
            return;
	    }
	    if (LOGGER.isLoggable(Level.FINE))
            LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep8OK3"),broker));
	    String operativeVhost = virtualHost.getVirtualHostName();
		
		//ensUsername subject + timestamp
	    Date requestTimestamp = requestType.getTimeStamp().toGregorianCalendar().getTime();
		String ensUsername = String.format(RABBITMQ_USERNAME_FORMAT,
		        subjectID,requestTimestamp);
		String ensPwd = pwdGen.getNextPassword();
		operativeHost = broker.getIpAddress();
		operativePort = broker.getTcpPortNumber();
		int httpPort = broker.getHttpPortNumber();
		String adminUsername = virtualHost.getBrokerUsername();
		String adminPwd = virtualHost.getBrokerPassword();
		
		UserService userService = rabbitMQUserSF.create(operativeHost, httpPort, adminUsername, adminPwd);
		
		//create user account
		List<String> listTags = new ArrayList<String>();
		listTags.add("ensUser");
		User ensUser;
		try {
		    ensUser = new User(ensUsername, ensPwd, listTags, false);

            PermissionService permissionService = rabbitMQPermissionSF.create(
                    operativeHost, httpPort, adminUsername, adminPwd);
            //merge the permissions
            boolean userAlreadyExists = userService.getUser(ensUsername) != null;
            Permission permissions;
            String destinationName;
            if (requestType.getOperation().equalsIgnoreCase(OPERATION_PUBLISH)) {
                destinationName = mNamespace.getNamespaceAMQPExchangeName();
                permissions = this.createPublishingPermissionsAndResources(userAlreadyExists, operativeHost, httpPort, operativePort, 
                    operativeVhost, ensUsername, ensPwd, adminUsername, adminPwd, permissionService, destinationName);
            } else {
                String exchangeName = mNamespace.getNamespaceAMQPExchangeName();
                destinationName = String.format(OPERATIVE_QUEUE_NAME_FORMAT, ensUsername,requestTimestamp);
                permissions = createSubscribingPermissionsAndResources(userAlreadyExists, operativeHost, httpPort, operativePort, 
                    operativeVhost, ensUsername, ensPwd, adminUsername, adminPwd, permissionService, destinationName, exchangeName);
            }
		    if (userAlreadyExists) {
                //delete the user
                //userService.deleteUser(ensUsername);
		        LOGGER.warning(MessageFormat.format(MESSAGES.getString("alreadyExistingCredentials"), ensUsername));
		    }
		    //create the user if everything is OK (i.e. the permission are not null)
		    if (permissions != null) {
    		    userService.putUser(ensUser);
                if (LOGGER.isLoggable(Level.FINE))
                    LOGGER.fine(MessageFormat.format("createdCredentials", ensUser.toString()));
                permissionService.putPermission(permissions);

                SuccessResponseDetails details = new SuccessResponseDetails();
                details.setSubjectID(ensUsername);
                details.setAccessToken(ensPwd);
                details.setVirtualHost(operativeVhost);
                details.setBrokerHost(operativeHost);
                details.setBrokerPort(operativePort);
                details.setQueueName(destinationName);
                //This might be a possible concurrency bottleneck 
                details.setSesionToken(UUID.randomUUID().toString());
                Date sessionTimeline = new Date();
                sessionTimeline.setTime(new Date().getTime() + SESSION_DURATION);
                Calendar calendar = new GregorianCalendar();
                calendar.setTime(sessionTimeline);
                details.setSessionExpiration(calendar.getTime());
                details.setUseTLS(true);

                responseType = ensResponseFactory.createSuccessResponse(
                        requestType.getRequestID(),requestType.getTimeStamp(),details);
		    }
		} catch (Exception e) {
		    FailureResponseDetails failureDetails = new FailureResponseDetails();
		    String reason = MESSAGES.getString(ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE);
		    failureDetails.setErrorReason(reason);
		    failureDetails.setErrorCode(ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE);
		    responseType = ensResponseFactory.createFailureResponse(
		            requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
		    if (LOGGER.isLoggable(Level.INFO))
		        LOGGER.log(Level.INFO, MessageFormat.format(MESSAGES.getString("evaluationStep9Failure"), 
		                ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE,reason,e.getMessage()), e);
		    return;
		}
	}
	
//	private class NamespaceInquirerThread implements Runnable {
//	    private boolean res;
//	    private NamespaceInquirer namespaceInquirer;
//	    private ResponseType responseType;
//	    private String namespace;
//	    private String routingKey;
//	    private ENSResponseFactoryException e;
//	    
//        /**
//         * @param res
//         * @param namespaceInquirer
//         * @param responseType
//         * @param namespace
//         * @param routingKey
//         * @param e
//         */
//        public NamespaceInquirerThread(
//                NamespaceInquirer namespaceInquirer,
//                String namespace, String routingKey) {
//            this.namespaceInquirer = namespaceInquirer;
//            this.namespace = namespace;
//            this.routingKey = routingKey;
//        }
//
//        /* (non-Javadoc)
//         * @see java.lang.Runnable#run()
//         */
//        @Override
//        public void run() {
//            boolean res = false;
//            
//            if (LOGGER.isLoggable(Level.FINE))
//                LOGGER.fine(MessageFormat.format(
//                        MESSAGES.getString("evaluationStep6"),namespace,routingKey));
//            
//            //boolean validNamespace = false;
//            try {
//                if(res = namespaceInquirer.patternExist(routingKey, namespace)){
//                    if (LOGGER.isLoggable(Level.FINE))
//                        LOGGER.fine(MessageFormat.format(
//                                MESSAGES.getString("evaluationStep6OK"), namespace, routingKey));
//                }else{
//                    FailureResponseDetails failureDetails = new FailureResponseDetails();
//                    String reason = MESSAGES.getString(ErrorCodes.NAMESPACE_EXISTENCE_NOT_EXIST);
//                    failureDetails.setErrorReason(reason);
//                    failureDetails.setErrorCode(ErrorCodes.NAMESPACE_EXISTENCE_NOT_EXIST);
//                    responseType = ensResponseFactory.createFailureResponse(
//                            requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
//                    if (LOGGER.isLoggable(Level.INFO))
//                        LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep6Failure"),
//                                ErrorCodes.NAMESPACE_EXISTENCE_NOT_EXIST,reason));
//                }
//            } catch (SyntaxPatternException e) {
//                FailureResponseDetails failureDetails = new FailureResponseDetails();
//                String reason = MESSAGES.getString(ErrorCodes.INVALID_NAMESPACE_PATTERN_SYNTAX);
//                failureDetails.setErrorReason(reason);
//                failureDetails.setErrorCode(ErrorCodes.INVALID_NAMESPACE_PATTERN_SYNTAX);
//                try {
//                    responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
//                } catch (ENSResponseFactoryException e1) {
//                    this.e = e1;
//                }
//                if (LOGGER.isLoggable(Level.INFO))
//                    LOGGER.log(Level.INFO, MessageFormat.format(MESSAGES.getString("evaluationStep6Failure"),
//                            ErrorCodes.INVALID_NAMESPACE_PATTERN_SYNTAX,reason),e);
//            } catch (NamespaceInquirerException e) {
//                FailureResponseDetails failureDetails = new FailureResponseDetails();
//                String reason = MESSAGES.getString(ErrorCodes.NAMESPACE_EXISTENCE_INTERNAL_ERROR);
//                failureDetails.setErrorReason(reason);
//                failureDetails.setErrorCode(ErrorCodes.NAMESPACE_EXISTENCE_INTERNAL_ERROR);
//                try {
//                    responseType = ensResponseFactory.createFailureResponse(
//                            requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
//                } catch (ENSResponseFactoryException e1) {
//                    this.e = e1;
//                }
//                if (LOGGER.isLoggable(Level.INFO))
//                    LOGGER.log(Level.INFO, MessageFormat.format(MESSAGES.getString("evaluationStep6Failure"),
//                            ErrorCodes.NAMESPACE_EXISTENCE_INTERNAL_ERROR,reason),e);
//            } catch (ENSResponseFactoryException e) {
//                this.e = e;
//            }
//            
//            //certificate control
//            if(res){
//                try {
//                    if (LOGGER.isLoggable(Level.FINE))
//                        LOGGER.fine(MESSAGES.getString("evaluationStep7"));
//                    
//                    Element rootAuCapDoc = getRootAuCapDoc();
//                    X509CertificateSubjectInfo x509CertificateSubjectInfo = 
//                            X509DocumentSigner.getX509CertificateSubjectInfo(rootAuCapDoc);
//                    String rootSubjectId = x509CertificateSubjectInfo.getX509IssuerName();
//                    String rootSerial = x509CertificateSubjectInfo.getX509SerialNumber().toString();
//                    
//                    if(res = namespaceInquirer.isTrustedCertificate(rootSubjectId,rootSerial, namespace)){
//                        if (LOGGER.isLoggable(Level.FINE))
//                            LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep7OK"),
//                                    rootSubjectId));
//                    }else{
//                        FailureResponseDetails failureDetails = new FailureResponseDetails();
//                        String reason = MESSAGES.getString(ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID);
//                        failureDetails.setErrorReason(reason);
//                        failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID);
//                        responseType = ensResponseFactory.createFailureResponse(
//                                requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
//                        if (LOGGER.isLoggable(Level.INFO))
//                            LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep7Failure"),
//                                    ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID,reason));
//                    }
//                } catch (NamespaceInquirerException e) {
//                    FailureResponseDetails failureDetails = new FailureResponseDetails();
//                    String reason = MESSAGES.getString(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
//                    failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
//                    try {
//                        responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(), 
//                                requestType.getTimeStamp(), failureDetails);
//                    } catch (ENSResponseFactoryException e1) {
//                        this.e = e1;
//                    }
//                    if (LOGGER.isLoggable(Level.INFO))
//                        LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7Failure"),
//                                ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID,reason),e);
//                } catch (GeneralSecurityException e) {
//                    FailureResponseDetails failureDetails = new FailureResponseDetails();
//                    String reason = MESSAGES.getString(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
//                    failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
//                    try {
//                        responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
//                                requestType.getTimeStamp(), failureDetails);
//                    } catch (ENSResponseFactoryException e1) {
//                        this.e = e1;
//                    }
//                    if (LOGGER.isLoggable(Level.INFO))
//                        LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7Failure"),
//                                ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID,reason),e);
//                } catch (MarshalException e) {
//                    FailureResponseDetails failureDetails = new FailureResponseDetails();
//                    String reason = MESSAGES.getString(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
//                    failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
//                    try {
//                        responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
//                                requestType.getTimeStamp(), failureDetails);
//                    } catch (ENSResponseFactoryException e1) {
//                        this.e = e1;
//                    }
//                    if (LOGGER.isLoggable(Level.INFO))
//                        LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7Failure"),
//                                ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID,reason),e);
//                } catch (CapabilitySchemaFactoryException e) {
//                    FailureResponseDetails failureDetails = new FailureResponseDetails();
//                    String reason = MESSAGES.getString(ErrorCodes.INTERNAL_ERROR);
//                    failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
//                    try {
//                        responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
//                                requestType.getTimeStamp(), failureDetails);
//                    } catch (ENSResponseFactoryException e1) {
//                        this.e = e1;
//                    }
//                    if (LOGGER.isLoggable(Level.INFO))
//                        LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7FailureDetails"),
//                                ErrorCodes.INTERNAL_ERROR,reason, MESSAGES.getString("rootCapabilityMarshallingFailure")),e);
//                } catch (CapabilitySchemaValidationHandlerException e) {
//                    FailureResponseDetails failureDetails = new FailureResponseDetails();
//                    String reason = MESSAGES.getString(ErrorCodes.INTERNAL_ERROR);
//                    failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
//                    try {
//                        responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
//                                requestType.getTimeStamp(), failureDetails);
//                    } catch (ENSResponseFactoryException e1) {
//                        this.e = e1;
//                    }
//                    if (LOGGER.isLoggable(Level.INFO))
//                        LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7FailureDetails"),
//                                ErrorCodes.INTERNAL_ERROR,reason, MESSAGES.getString("rootCapabilityMarshallingFailure")),e);
//                } catch (ENSResponseFactoryException e) {
//                    this.e = e;
//                }
//            }
//        }
//
//        /**
//         * @return the res
//         */
//        public boolean isRes() {
//            return res;
//        }
//
//        /**
//         * @return the responseType
//         */
//        public ResponseType getResponseType() {
//            return responseType;
//        }
//
//        /**
//         * @return the e
//         */
//        public ENSResponseFactoryException getE() {
//            return e;
//        }
//	    
//	}
	
	private boolean executeNamespaceCapabilityCheck() throws ENSResponseFactoryException {
//	    NamespaceInquirerThread t = new NamespaceInquirerThread(
//	            namespaceInquirer, namespace, routingKey);
//	    Future<?> f = THREAD_POOL.submit(t);
//	    try {
//            f.get();
//        } catch (Throwable e) {
//            FailureResponseDetails failureDetails = new FailureResponseDetails();
//            String reason = "Thread";
//            failureDetails.setErrorCode(ErrorCodes.INTERNAL_ERROR);
//            responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
//                    requestType.getTimeStamp(), failureDetails);
//            if (LOGGER.isLoggable(Level.INFO))
//                LOGGER.log(Level.INFO,reason, e);
//            return false;
//        }
//	    if (t.getE() != null)
//	        throw t.getE();
//	    responseType = t.getResponseType();
//	    return t.isRes();
	    
		boolean res = false;
        
        if (LOGGER.isLoggable(Level.FINE))
		    LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep6"),namespace,routingKey));
		
		//boolean validNamespace = false;
        
		try {
		    if (namespaceInquirer == null) {
	            if (LOGGER.isLoggable(Level.FINE))
	                LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep6OK"), namespace, routingKey));
	            res = true;
	        } else if(res = namespaceInquirer.patternExist(routingKey, namespace)){
				if (LOGGER.isLoggable(Level.FINE))
		            LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep6OK"), namespace, routingKey));
			}else{
	            FailureResponseDetails failureDetails = new FailureResponseDetails();
				String reason = MESSAGES.getString(ErrorCodes.NAMESPACE_EXISTENCE_NOT_EXIST);
				failureDetails.setErrorReason(reason);
				failureDetails.setErrorCode(ErrorCodes.NAMESPACE_EXISTENCE_NOT_EXIST);
				responseType = ensResponseFactory.createFailureResponse(
				        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
				if (LOGGER.isLoggable(Level.INFO))
				    LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep6Failure"),
				            ErrorCodes.NAMESPACE_EXISTENCE_NOT_EXIST,reason));
			}
		} catch (SyntaxPatternException e) {
			FailureResponseDetails failureDetails = new FailureResponseDetails();
            String reason = MESSAGES.getString(ErrorCodes.INVALID_NAMESPACE_PATTERN_SYNTAX);
            failureDetails.setErrorReason(reason);
			failureDetails.setErrorCode(ErrorCodes.INVALID_NAMESPACE_PATTERN_SYNTAX);
			responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
			if (LOGGER.isLoggable(Level.INFO))
                LOGGER.log(Level.INFO, MessageFormat.format(MESSAGES.getString("evaluationStep6Failure"),
                        ErrorCodes.INVALID_NAMESPACE_PATTERN_SYNTAX,reason),e);
		} catch (NamespaceInquirerException e) {
			FailureResponseDetails failureDetails = new FailureResponseDetails();
            String reason = MESSAGES.getString(ErrorCodes.NAMESPACE_EXISTENCE_INTERNAL_ERROR);
            failureDetails.setErrorReason(reason);
			failureDetails.setErrorCode(ErrorCodes.NAMESPACE_EXISTENCE_INTERNAL_ERROR);
			responseType = ensResponseFactory.createFailureResponse(
			        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
			if (LOGGER.isLoggable(Level.INFO))
                LOGGER.log(Level.INFO, MessageFormat.format(MESSAGES.getString("evaluationStep6Failure"),
                        ErrorCodes.NAMESPACE_EXISTENCE_INTERNAL_ERROR,reason),e);
		}
		
		//certificate control
		if(res){
			try {
				if (LOGGER.isLoggable(Level.FINE))
				    LOGGER.fine(MESSAGES.getString("evaluationStep7"));
				
				Element rootAuCapDoc = getRootAuCapDoc();
				X509CertificateSubjectInfo x509CertificateSubjectInfo = 
				        X509DocumentSigner.getX509CertificateSubjectInfo(rootAuCapDoc);
				String rootSubjectId = x509CertificateSubjectInfo.getX509IssuerName();
				String rootSerial = x509CertificateSubjectInfo.getX509SerialNumber().toString();
				
				if (namespaceInquirer == null) {
				    if (LOGGER.isLoggable(Level.FINE))
                        LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep7OK"),
                                rootSubjectId));
				    res = true;
				}else if(res = namespaceInquirer.isTrustedCertificate(rootSubjectId,rootSerial, namespace)){
				    if (LOGGER.isLoggable(Level.FINE))
	                    LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep7OK"),
	                            rootSubjectId));
				}else{
					FailureResponseDetails failureDetails = new FailureResponseDetails();
					String reason = MESSAGES.getString(ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID);
					failureDetails.setErrorReason(reason);
					failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID);
					responseType = ensResponseFactory.createFailureResponse(
					        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
					if (LOGGER.isLoggable(Level.INFO))
		                LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep7Failure"),
		                        ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID,reason));
				}
			} catch (NamespaceInquirerException e) {
				FailureResponseDetails failureDetails = new FailureResponseDetails();
				String reason = MESSAGES.getString(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
				failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
				responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(), 
				        requestType.getTimeStamp(), failureDetails);
				if (LOGGER.isLoggable(Level.INFO))
                    LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7Failure"),
                            ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID,reason),e);
			} catch (GeneralSecurityException e) {
			    FailureResponseDetails failureDetails = new FailureResponseDetails();
                String reason = MESSAGES.getString(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
                failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
                responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
                        requestType.getTimeStamp(), failureDetails);
                if (LOGGER.isLoggable(Level.INFO))
                    LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7Failure"),
                            ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID,reason),e);
			} catch (MarshalException e) {
			    FailureResponseDetails failureDetails = new FailureResponseDetails();
                String reason = MESSAGES.getString(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
                failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
                responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
                        requestType.getTimeStamp(), failureDetails);
                if (LOGGER.isLoggable(Level.INFO))
                    LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7Failure"),
                            ErrorCodes.CERTIFICATE_VERIFIER_NOT_VALID,reason),e);
			} catch (CapabilitySchemaFactoryException e) {
			    FailureResponseDetails failureDetails = new FailureResponseDetails();
                String reason = MESSAGES.getString(ErrorCodes.INTERNAL_ERROR);
                failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
                responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
                        requestType.getTimeStamp(), failureDetails);
                if (LOGGER.isLoggable(Level.INFO))
                    LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7FailureDetails"),
                            ErrorCodes.INTERNAL_ERROR,reason, MESSAGES.getString("rootCapabilityMarshallingFailure")),e);
            } catch (CapabilitySchemaValidationHandlerException e) {
                FailureResponseDetails failureDetails = new FailureResponseDetails();
                String reason = MESSAGES.getString(ErrorCodes.INTERNAL_ERROR);
                failureDetails.setErrorCode(ErrorCodes.CERTIFICATE_VERIFIER_FAILURE);
                responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(),
                        requestType.getTimeStamp(), failureDetails);
                if (LOGGER.isLoggable(Level.INFO))
                    LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep7FailureDetails"),
                            ErrorCodes.INTERNAL_ERROR,reason, MESSAGES.getString("rootCapabilityMarshallingFailure")),e);
            }
		}
		return res;
	}
	
	private Element getRootAuCapDoc() throws CapabilitySchemaFactoryException, CapabilitySchemaValidationHandlerException {
	    AccessRightsCapabilityType issuerAuCap = capability;
	    //debugging
//        try {
//            X509CertificateSubjectInfo x509CertificateSubjectInfo = X509DocumentSigner.getX509CertificateSubjectInfo(
//                CapabilitySchemaFactory.getInstance().createAccessRightsDocument(issuerAuCap).getDocumentElement());
//            String rootSubjectId = x509CertificateSubjectInfo.getX509IssuerName();
//            String rootSerial = x509CertificateSubjectInfo.getX509SerialNumber().toString();
//            LOGGER.fine("init " + x509CertificateSubjectInfo.getX509SubjectName() + 
//                    " " + rootSubjectId + " " + rootSerial);
//        } catch (GeneralSecurityException e) {
//            e.printStackTrace();
//        } catch (MarshalException e) {
//            e.printStackTrace();
//        }
        
//        issuerAuCap = capability;
	    while (issuerAuCap.getIssuerAccessRightsCapability().getAccessRightsCapability() != null) {
	        issuerAuCap = issuerAuCap.getIssuerAccessRightsCapability().getAccessRightsCapability();
	        //debugging
//            try {
//                X509CertificateSubjectInfo x509CertificateSubjectInfo = X509DocumentSigner.getX509CertificateSubjectInfo(
//                    CapabilitySchemaFactory.getInstance().createAccessRightsDocument(issuerAuCap).getDocumentElement());
//                String rootSubjectId = x509CertificateSubjectInfo.getX509IssuerName();
//                String rootSerial = x509CertificateSubjectInfo.getX509SerialNumber().toString();
//                LOGGER.fine("loop " + x509CertificateSubjectInfo.getX509SubjectName() + 
//                        " " + rootSubjectId + " " + rootSerial);
//            } catch (GeneralSecurityException e) {
//                e.printStackTrace();
//            } catch (MarshalException e) {
//                e.printStackTrace();
//            }
	    }
	    return CapabilitySchemaFactory.getInstance().createAccessRightsDocument(issuerAuCap).getDocumentElement();
	}

	private boolean executePDPCapabilityCheck() throws ENSResponseFactoryException{		
		boolean res = false;
		try {
		    if (LOGGER.isLoggable(Level.FINE))
		        LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep5"),pdpClient.getURL().toString()));
//			if(!pdpClient.executePDPCapabilityEvaluation(capability)){
			if(!pdpClient.executePDPCapabilityEvaluation(requestType.getAccessRightsCapability().getCapability())){
				FailureResponseDetails failureDetails = new FailureResponseDetails();
				String reason = MESSAGES.getString(ErrorCodes.PDP_EVALUATION_NOT_VALID);
				failureDetails.setErrorReason(reason);
				failureDetails.setErrorCode(ErrorCodes.PDP_EVALUATION_NOT_VALID);
				responseType = ensResponseFactory.createFailureResponse(
				        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
				if (LOGGER.isLoggable(Level.INFO))
				    LOGGER.info(MessageFormat.format(MESSAGES.getString("evaluationStep5Failure"),
				            ErrorCodes.PDP_EVALUATION_NOT_VALID,reason));
			}else
				res = true;
		}  catch (Exception e) {
			FailureResponseDetails failureDetails = new FailureResponseDetails();
			String reason = MESSAGES.getString(ErrorCodes.PDP_EVALUATION_FAILURE);
            failureDetails.setErrorReason(reason );
			failureDetails.setErrorCode(ErrorCodes.PDP_EVALUATION_FAILURE);
			responseType = ensResponseFactory.createFailureResponse(
			        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
			if (LOGGER.isLoggable(Level.INFO))
			    LOGGER.log(Level.INFO, MessageFormat.format(MESSAGES.getString("evaluationStep5Failure"),
			            ErrorCodes.PDP_EVALUATION_FAILURE, reason), e);
		}
		return res;
	}

	/**
	 * 
	 * @param capability
	 * @param vcs
	 * @return
	 * @throws ENSResponseFactoryException 
	 * @throws ENSServiceFactoryException 
	 */
	private boolean executeCapabilityVerifierCheck() throws ENSResponseFactoryException{
		boolean res = false;
        if (LOGGER.isLoggable(Level.FINE))
            LOGGER.fine(MESSAGES.getString("evaluationStep4"));
        
		try {
			if (!CapabilityVerifier.accessRightsCapabilityValidityCheck(capability, new ArrayList<VerifiedCapability>())) {
				FailureResponseDetails failureDetails = new FailureResponseDetails();
				String reason = MESSAGES.getString(ErrorCodes.CAPABILITY_VERIFIER_NOT_VALID);
                failureDetails.setErrorReason(reason );
				failureDetails.setErrorCode(ErrorCodes.CAPABILITY_VERIFIER_NOT_VALID);
				responseType = ensResponseFactory.createFailureResponse(
				        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
				if (LOGGER.isLoggable(Level.INFO))
				    LOGGER.log(Level.INFO, MessageFormat.format(MESSAGES.getString("evaluationStep4Failure"),
				            ErrorCodes.CAPABILITY_VERIFIER_NOT_VALID,reason));
			} else {
				res = true;
			}
		} catch (CapabilitySchemaValidationHandlerException e) {
			//String errors = CapabilitySchemaValidationHandler.dumpValidationEvents(e.getValidationEvents());
			FailureResponseDetails failureDetails = new FailureResponseDetails();
			String reason = MESSAGES.getString(ErrorCodes.INVALID_CAPABILITY_TOKEN);
			failureDetails.setErrorReason(reason);
			failureDetails.setErrorCode(ErrorCodes.INVALID_CAPABILITY_TOKEN);
			responseType = ensResponseFactory.createFailureResponse(
			        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
			LOGGER.log(Level.INFO,MessageFormat.format("evaluationStep4Failure",
			        ErrorCodes.CAPABILITY_VERIFIER_FAILURE,reason),e);
		} catch (CapabilityVerifierException e) {
			FailureResponseDetails failureDetails = new FailureResponseDetails();
			String reason = ErrorCodes.CAPABILITY_VERIFIER_FAILURE;
			failureDetails.setErrorReason(reason);
			failureDetails.setErrorCode(ErrorCodes.CAPABILITY_VERIFIER_FAILURE);
			responseType = ensResponseFactory.createFailureResponse(
			        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
			LOGGER.log(Level.INFO,MessageFormat.format("evaluationStep4Failure",
			        ErrorCodes.CAPABILITY_VERIFIER_FAILURE, reason),e);
		}
		
		return res;
	}
	
//	private AccessRightsCapabilityType getRootAuCap(AccessRightsCapabilityType capability) {
//		AccessRightsCapabilityType parentAuCap = capability;
//		AccessRightsCapabilityType currentCapability = capability;
//		boolean stop = false;
//		AccessRightsCapabilityType rootAuCap = null;
//		do {
//			System.out.println(capability);
//		    parentAuCap = currentCapability.getIssuerAccessRightsCapability().getAccessRightsCapability();
//		    if (parentAuCap == null) {
//		        rootAuCap = currentCapability;
//		        stop = true;
//		    }
//	        else
//	            currentCapability = parentAuCap;
//		} while (!stop);
//		
//		return rootAuCap;
//	}

	private Permission createSubscribingPermissionsAndResources(boolean userAlreadyExists, String operativeHost, 
	        int httpPort,int operativePort, String operativeVhost,String ensUsername, String ensPwd,
	        String adminUsername, String adminPwd, PermissionService permissionService, String queueName,
	        String exchangeName)
    throws ENSResponseFactoryException {
	    if (LOGGER.isLoggable(Level.FINE))
            LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep9ExplainationS"), 
                    ensUsername, namespace, routingKey));
        Connection adminConnection = null;
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUsername(adminUsername);
        factory.setPassword(adminPwd);
        factory.setVirtualHost(operativeVhost);
        factory.setHost(operativeHost);
        factory.setPort(operativePort);
        try {
            adminConnection = factory.newConnection();
            Channel channel = adminConnection.createChannel();
            //check if the queue exists
            if(AMQPQueueHelper.exists(channel, queueName)){
                FailureResponseDetails failureDetails = new FailureResponseDetails();
                String reason = ErrorCodes.INTERNAL_ERROR;
                failureDetails.setErrorReason(reason);
                failureDetails.setErrorCode(ErrorCodes.INTERNAL_ERROR);
                responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(), 
                        requestType.getTimeStamp(), failureDetails);
                if (LOGGER.isLoggable(Level.INFO))
                    LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep9Failure"), 
                            ErrorCodes.INTERNAL_ERROR,reason,
                            MessageFormat.format(MESSAGES.getString("alreadyExistingQueue"),
                            queueName, operativeVhost, operativeHost + ":" + operativePort)));
                return null;
            }
            //forcing creating a new Connection because the following exception is thrown even on new Channel
//            com.rabbitmq.client.AlreadyClosedException: clean connection shutdown; reason: Attempt to use closed channel
//            at com.rabbitmq.client.impl.AMQChannel.ensureIsOpen(AMQChannel.java:190)
//            at com.rabbitmq.client.impl.AMQChannel.rpc(AMQChannel.java:223)
//            at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:209)
//            at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:118)
//            at com.rabbitmq.client.impl.ChannelN.queueDeclare(ChannelN.java:766)
//            at com.rabbitmq.client.impl.ChannelN.queueDeclare(ChannelN.java:61)
//            at it.txt.ens.authorisationService.amqp.AMQPQueueHelper.creates(AMQPQueueHelper.java:42)
//            at it.txt.ens.authorisationService.util.AccessRequestEvaluator.createSubscribingPermissionsAndResources(AccessRequestEvaluator.java:893)
//            at it.txt.ens.authorisationService.util.AccessRequestEvaluator.executeRabbitMQOperations(AccessRequestEvaluator.java:499)
//            at it.txt.ens.authorisationService.util.AccessRequestEvaluator.evaluate(AccessRequestEvaluator.java:284)
//            at it.txt.ens.authorisationService.util.AccessRequestEvaluator.run(AccessRequestEvaluator.java:216)
//            at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
//            at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
//            at java.util.concurrent.FutureTask.run(Unknown Source)
//            at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
//            at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
//            at java.lang.Thread.run(Unknown Source)
            adminConnection.close();
            adminConnection = factory.newConnection();
            AMQPQueueHelper.creates(adminConnection.createChannel(), queueName, false, false, true);
            if (LOGGER.isLoggable(Level.FINE))
                LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep9OK-1S"),
                        queueName, operativeVhost, operativeHost + ":" + operativePort));
            adminConnection.createChannel().queueBind(queueName,exchangeName,routingKey);
            if (LOGGER.isLoggable(Level.FINE))
                LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep9OK-2S"),
                        queueName, exchangeName));
//            if (userAlreadyExists)
//                return mergePermissions(permissionService , operativeVhost, ensUsername, "", "", queueName);
//            else
            return new Permission(operativeVhost, ensUsername, "", "", queueName);
        } catch (Exception e) {
            FailureResponseDetails failureDetails = new FailureResponseDetails();
            String reason = MESSAGES.getString(ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE);
            failureDetails.setErrorReason(reason);
            failureDetails.setErrorCode(ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE);
            responseType = ensResponseFactory.createFailureResponse(requestType.getRequestID(), 
                    requestType.getTimeStamp(), failureDetails);
            if (LOGGER.isLoggable(Level.INFO))
                LOGGER.log(Level.INFO,MessageFormat.format(MESSAGES.getString("evaluationStep9Failure"), 
                        ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE,reason, e.getMessage()),e);
            return null;
        } finally {
            if (adminConnection != null)
                try {
                    adminConnection.close();
                } catch (IOException e) { }
        }
    }
	
	private Permission createPublishingPermissionsAndResources(boolean userAlreadyExists, 
	        String operativeHost, int httpPort, int operativePort, 
	        String operativeVhost,String ensUsername, String ensPwd, 
	        String adminUsername, String adminPwd, PermissionService permissionService,
	        String exchangeName)
    throws ENSResponseFactoryException {
		if (LOGGER.isLoggable(Level.FINE))
		    LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep9ExplainationP"), 
		            ensUsername, namespace, routingKey));

        AMQPConnectionHelper mc = new AMQPConnectionHelper();
		Connection adminConnection = null;
        try {
        	adminConnection = mc.connection(adminUsername, adminPwd, 
			        operativeVhost, operativeHost, operativePort);
        	Channel channel = adminConnection.createChannel();
			//check if the exchange exists
	        AMQPExchangeHelper exchHelper = new AMQPExchangeHelper();
	        if(!exchHelper.exists(channel, exchangeName)){
	        	exchHelper.creates(channel, exchangeName, "topic", true, false);
	        	if (LOGGER.isLoggable(Level.FINE))
	        	    LOGGER.fine(MessageFormat.format(MESSAGES.getString("evaluationStep9OK-1P"), 
	        	            exchangeName, operativeVhost,operativeHost + ":" + operativePort));
	        }else if (LOGGER.isLoggable(Level.FINE)){
	        	LOGGER.fine(MESSAGES.getString("evaluationStep9OK-2P"));
	        }
//	        if (userAlreadyExists)
//	            return mergePermissions(permissionService , operativeVhost, ensUsername, "", exchangeName, "");
//	        else
            return new Permission(operativeVhost, ensUsername, "", exchangeName, "");
		} catch (IOException e) {
			FailureResponseDetails failureDetails = new FailureResponseDetails();
			String reason = MESSAGES.getString(ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE);
			failureDetails.setErrorReason(reason);
			failureDetails.setErrorCode(ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE);
			responseType = ensResponseFactory.createFailureResponse(
			        requestType.getRequestID(), requestType.getTimeStamp(), failureDetails);
			if (LOGGER.isLoggable(Level.INFO))
			    LOGGER.log(Level.INFO, MessageFormat.format(MESSAGES.getString("evaluationStep9Failure"),
			            ErrorCodes.ENS_RESOURCE_RESERVATION_FAILURE,reason),e);
			return null;
		} finally {
		    if (adminConnection != null)
                try {
                    adminConnection.close();
                } catch (IOException e) { }
		}
	}
	@Override
    public void shutdownCompleted(ShutdownSignalException cause) {
	    if (!isClosed)
	        LOGGER.log(Level.SEVERE, MessageFormat.format(MESSAGES.getString("channelShutdown"),
                connection.getAddress().getHostName() + ":" + connection.getPort()), cause);
    }
}